﻿#region Copyright 2009-2010 by Roger Knapp, Licensed under the Apache License, Version 2.0
/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#endregion
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using CSharpTest.Net.Commands;
using CSharpTest.Net.CustomTool.CodeGenerator;
using CSharpTest.Net.CustomTool.Projects;
using CSharpTest.Net.Utils;

namespace CSharpTest.Net.CustomTool
{
	class Program
	{
		private static int Main(string[] args)
		{
			int result = -1;
		
			string ignore;
			bool wait = ArgumentList.Remove(ref args, "wait", out ignore);

			Log.ConsoleLevel = TraceLevel.Info;
			if (ArgumentList.Remove(ref args, "quiet", out ignore))
				Quiet();
			if (ArgumentList.Remove(ref args, "verbose", out ignore))
				Verbose();

			using(Log.Start(Environment.CommandLine))
			try
			{
				CommandInterpreter ci = new CommandInterpreter(DefaultCommands.Help, typeof(Program));
				ci.Run(args);
				result = ci.ErrorLevel;
			}
			catch (OperationCanceledException)
			{ result = 3; }
			catch (ApplicationException ex)
			{
				Log.Error(ex);
				Console.Error.WriteLine(ex);
				result = 1;
			}
			catch (Exception ex)
			{
				Log.Error(ex);
				Console.Error.WriteLine(ex.ToString());
				result = 2;
			}
			finally
			{
				if (wait)
				{
					Console.WriteLine("Exited with result = {0}, Press Enter to quit.", result);
					Console.ReadLine();
				}
			}
			return Environment.ExitCode = result;
		}

		[Command("register", Description="Registers the CmdTool custom generator with Visual Studio 2005, 2008, and 2010")]
		public static void Register()
		{
			try
			{
				RegistrationServices svc = new RegistrationServices();
				svc.RegisterAssembly(typeof (Program).Assembly, AssemblyRegistrationFlags.SetCodeBase);
			}
			catch (Exception e) { throw new ApplicationException(e.Message, e); }
		}

		[Command("unregister", Description = "Removes the CmdTool custom generator from Visual Studio 2005, 2008, and 2010")]
		public static void Unregister()
		{
			try
			{
				RegistrationServices svc = new RegistrationServices();
				svc.UnregisterAssembly(typeof(Program).Assembly);
			}
			catch (Exception e) { throw new ApplicationException(e.Message, e); }
		}

		[Command("clean", Description = "Removes all output generated by the projects specified")]
		public static void Clean(
			[Argument("projects", Description = "One or more projects or directories to scan for projects")]
		    [AllArguments] string[] projectFiles)
		{
			string value;
			CmdToolBuilder builder = new CmdToolBuilder();
			Visitor(ArgumentList.Remove(ref projectFiles, "fast", out value), projectFiles, builder.Clean);
		}

		[Command("build", Description = "Rebuilds the output generated by the projects specified")]
		public static void Build(
			ICommandInterpreter ci,
			[Argument("projects", Description = "One or more projects or directories to scan for projects")]
			[AllArguments] string[] projectFiles)
		{
			string value;
			CmdToolBuilder builder = new CmdToolBuilder();
			Visitor(ArgumentList.Remove(ref projectFiles, "fast", out value), projectFiles, builder.Generate);
		}
        
		[Command("help-vars", Description = "Outputs the available replacment variables for the first generated file in the provided project")]
		public static void HelpVariables(
			[Argument("projects", Description = "One or more projects or directories to scan for projects")]
			[AllArguments] string[] projectFiles)
		{
			string value;
			Visitor(ArgumentList.Remove(ref projectFiles, "fast", out value), projectFiles, 
				delegate(IGeneratorArguments args) { Console.WriteLine(args.Help()); throw new OperationCanceledException(); });
		}

		[Command("help-config", Description = "Outputs a sample configuration file that demonstrates the available options.")]
		public static void HelpConfig()
		{
			Console.WriteLine(
				new StreamReader(
					typeof(Program).Assembly.GetManifestResourceStream(typeof(Program).Namespace + ".CmdTool.config")
					).ReadToEnd()
				);
		}

		[Command("/verbose", Description = "Avaliable for all commands to provide verbose output to the console.")]
        public static void Verbose() { Log.ConsoleLevel = TraceLevel.Verbose; Config.VERBOSE = true; }
		[Command("/quiet", Description = "Avaliable for all commands to limit output to the console to warnings and errors only.")]
		public static void Quiet() { Log.ConsoleLevel = TraceLevel.Warning; }

		private static void Visitor(bool fast, string[] projectFiles, Action<IGeneratorArguments> func)
		{
			ProjectVisitor projects = new ProjectVisitor(fast, projectFiles);
			Check.Assert<ApplicationException>(projects.Count > 0, "Unable to locate any projects matching: " + String.Join(" ", projectFiles));

			projects.VisitProjectItems(
			delegate(IProjectInfo p, IProjectItem item)
			{
				if (!StringComparer.OrdinalIgnoreCase.Equals("CmdTool", item.CustomTool))
					return;
				string itemPath = item.FullFileName;
				if (File.Exists(itemPath))
				{
					GeneratorArguments args = new GeneratorArguments(true, itemPath, item, p);
					args.OutputMessage += OutputMessage;
					func(args);
				}
			}
			);
		}

		private static void OutputMessage(string message)
		{
			Match match = RegexPatterns.VSErrorMessage.Match(message);
			if (match.Success && File.Exists(match.Groups["path"].Value))
			{
				if (match.Groups["error"].Success)
					Log.Error(message);
				else if (match.Groups["warning"].Success)
					Log.Warning(message);
				else
					Log.Info(message);
			}
			else Log.Info(message);
		}
	}
}
